/*
 * =====================================================================================
 *
 *       Filename:  echo_select_tcp_svr.cpp
 *
 *    Description:  使用select处理tcp连接
 *
 *        Version:  1.0
 *        Created:  2008年09月01日 17时29分49秒
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  ZhixiongHong (hzx), fishwarter@gmail.com
 *        Company:  Unioncast
 *
 * =====================================================================================
 */

#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>


#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <iostream>



using namespace std;

#define SERV_PORT		9800
#define LISTENQ			16
#define MAX_LINE		2048

void str_echo(int sockfd);

void sig_chld(int signo)
{
	pid_t		pid;
	int			stat;
	while( (pid = waitpid(-1, &stat, WNOHANG))> 0)
		printf("child %d terminated\n", pid);

	return ;
}

int main(int argc, char** argv)
{
	if( argc != 2)
	{
		cout << "usage: " << argv[0] << "  listenport" << endl;
		return -1;
	}
	unsigned short listenport = atoi(argv[1]);	
	signal(SIGCHLD, sig_chld);
	signal(SIGPIPE, SIG_IGN);	

	int					i, maxi, maxfd, listenfd, connfd, sockfd;
	int 				nready, client[FD_SETSIZE];
	ssize_t				n;
	fd_set				rset, allset;
	char				buf[MAX_LINE];
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;	
	
	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if( listenfd < 0)
	{
		cout << "create listenfd error " << strerror(errno) << endl;
		return -1;
	}
	
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(listenport);
	
	const int on = 1;
	int retint = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if( retint == -1)
	{
		cout << "set socket SO_REUSEADDR  error " << strerror(errno) << endl;
		return -1;
	}
	
	retint = bind(listenfd, (sockaddr *)&servaddr, sizeof(servaddr));
	if( retint == -1)
	{
		cout << "bind socket error:  " << strerror(errno) << endl;
		return -1;
	}
		
	retint = listen(listenfd, LISTENQ);
	if( retint == -1)
	{
		cout << "listen error " << strerror(errno) << endl;
		return -1;
	}
	
	maxfd = listenfd;
	maxi = -1;
	for(i = 0; i < FD_SETSIZE; i++)
		client[i] = -1;	
	
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);	
	
	cout << "start server at " << SERV_PORT << endl;
	for(;;)
	{
		rset = allset;
		
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);  // 等待某件事情的发生
		
		if(FD_ISSET(listenfd, &rset)) // new client connection
		{
			clilen = sizeof(cliaddr);
			connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
			cout << "new connection" << endl;
			for(i = 0; i < FD_SETSIZE; i++)
			{
				if( client[i] < 0)
				{
					client[i] = connfd;
					break;
				}
			}	
			if( i == FD_SETSIZE)
			{
				cout << "**********************too many clients" << endl;
			}
			
			FD_SET(connfd, &allset);
			if( connfd > maxfd)
				maxfd = connfd;
			if( i > maxi )
				maxi = i;

			if( --nready <= 0)
			{
				cout << "+++++++++++++++++++++++nready just one " << endl;
				continue;
			}
		}
		for( i = 0; i < maxi; i ++ ) /* check all clients for data  */
		{
			sockfd = client[i];
			if( sockfd < 0)
				continue;
			if( FD_ISSET(sockfd, &rset) )
			{
				n = read(sockfd, buf, MAX_LINE);
				if( n == 0)
				{
					close(sockfd);
					FD_CLR(sockfd, &allset);	
					client[i] = -1;
				}
				else
				{
					cout << "sockfd = " << sockfd << " get buf " << buf << " n = " <<  n << " strlen(buf) = " << strlen(buf) << endl;
					write(sockfd, buf, n);
				}
				if( --nready <= 0 )
					break;
			}
		}
	}

}

